/*
 * =====================================================================================
 *
 *       Filename:  signal.c
 *
 *    Description:  this is the origin signal program, it is not easy to understand.
 *
 *        Version:  1.0
 *        Created:  2011年03月15日 10时41分29秒
 *       Compiler:  gcc
 *
 *         Author:  lonelydragon1988@gmail.com 
 *        Company:  it is a long time since last time i check it
 *
 * =====================================================================================
 */


int sys_sgetmask()
{
	return current->blocked;
}


int sys_ssetmask(int newmask)
{
	int old=current->blocked;
	current->block=newmask&~(1<<(SIGKILL-1));
	return old;
}
static inline void save_old(char *from,char *to)
{
	int i;
	verify_area(to,sizeof(struct sigaction));
	for(i=0;i<sizeof(struct sigaction );i++)
	{
		put_fs_byte(*from,to);
		from++;
		to++;
	}

}
static inline void get_new(char *from,char *to)
{
	int i;
	for(i=0;i<sizeof(struct sigaction);i++)
		*(to++)=get_fs_byte(from++);
}
/*
 * sys_signal 函数用来处理信号量 但具体过程实在让人迷惑。
 * 指针型函数需要好好再看一遍。这实在是个很让人头腾的问题
 */
int sys_signal(int signum,long handler,long restorer)
{
	struct sigaction tmp;
	if(signum<1 || signum >32 || signum ==SIGKILL)
		return -1;
	tmp.sa_handler=(void (*int))handler;
	tmp.sa_mask=0;
	tmp.sa_flags=SA_ONESHOT | SA_NOMASK;
	tmp.sa_restorer=(void (*)(void))restorer;

	handler=(long )current->sigaction[signum-1].sa_handler;
	current->sigaction[signum-1]=tmp;
	return handler;
}


int sys_sigaction(int signum,const struct sigaction *action,struct sigaction *oldaction)
{
	struct sigaction tmp;
	//信号量只设定了32个数值，故而不能超过32 而sigkill是不可以更改的信号量
	if(signum<1 || signum>32 || signum==SIGKILL)
		return -1;
	tmp=current->sigaction[signum-1];
	get_new((char *)action,
			(char *)(signum-1+current->sigaction));
	if(oldaction)
		save_old((char*)&tmp,(char *)oldaction);
	//如果允许信号在自己的信号句柄中收到，责令屏蔽码为0,否则设置屏蔽本信号
	if(current->sigaction[signum-1].sa_flags&SA_NOMASK)
		current->sigaction[signum-1].sa_mask=0;
	else
		current->sigaction[signum-1].sa_mask |= (1<<(signum-1));
	return 0;
}

/*
 * 本函数是本个文件中最重要的一个，主要用来处理信号量，并将一些信息压入用户栈
 */
void do_signal(long signr,long eax,long ebx,long ecx,long edx,long fd,long es,long ds,long eip,
		long cs,long eflags,unsigned long *esp,long ss)

{

	unsigned long sa_handler;
	long old_eip=eip;
	struct sigaction *sa =current->sigaction+signum-1;

	int longs;
	unsigned long *tmp_esp;
	sa_handler=(unsigned long)sa->sa_handler;
	if(sa_handler==1)
		return;
	if(!sa_handler)
	{
		if(signr=SIGCHLD)
			return;
		else
			do_exit(1<<(signr-1));
	}
	if(sa->sa_flags & SA_ONESHOT)
		sa->sa_handler=NULL;
	*(&eip)=sa_handler;
	longs=(sa->sa_flags & SA_NOMASK)?7:8;
	*(&eip)-=longs;
	verify_area(esp,longs*4);
	tmp_esp=esp;
	/*
	 * 用来设定新的寄存器参数
	 */
	put_fs_long((long)sa->sa_restorer,tmp_esp++);
	put_fs_long(signr,tmp_esp++);
	if(!(sa->sa_flags & SA_NOMASK))
		put_fs_long(current->blocked,tmp_esp++);
	put_fs_long(eax,tmp_esp++);
	put_fs_long(ecx,tmp_esp++);
	put_fs_long(edx,tmp_esp++);
	put_fs_long(eflags,tmp_esp++);
	put_fs_long(old_eip,tmp_esp++);
	current->blocked | =sa->sa_mask;

}
